﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
using System.Reflection;

namespace EASM_Language.Old
{
    class Program
    {
        static int Main(string[] args)
        {
            if (args.Length > 0)
            {
                #region Execution
                int returnCode = 0;
                bool breakCode = false;
                Dictionary<string, string> vars = new Dictionary<string, string>();
                Dictionary<string, string> regs = new Dictionary<string, string>();
                Dictionary<string, int> labels = new Dictionary<string, int>();
                object eax = "1", ebx = "0", ecx = "", edx = "";
                FileStream outfs = null;
                StreamWriter outsw = null;
                TextWriter outold = null;
                short mode = 0; // 0 = none, 1 = .data, 2 = .code
                string lasm = "";
                string file = args[0], outfile = "";
                bool output = false, exec = false;
                if (args.Length == 2 && args[0] != "-exec")
                {
                    output = true;
                    outfile = args[1];
                    outold = Console.Out;
                    outfs = new FileStream(outfile, FileMode.OpenOrCreate);
                    outsw = new StreamWriter(outfs);
                    Console.SetOut(outsw);
                }
                if (args[0] == "-exec")
                {
                    try
                    {
                        exec = true;
                        FileStream efs = new FileStream(args[1], FileMode.Open);
                        StreamReader esr = new StreamReader(efs);
                        string encodedFile = esr.ReadToEnd();
                        foreach (string line in encodedFile.Split('\n'))
                        {
                            lasm += Open(line) + '\n';
                        }
                        esr.Close();
                        efs.Close();
                    }
                    catch (Exception e) { }
                }
                if (!exec)
                {
                    FileStream fs = new FileStream(file, FileMode.Open);
                    StreamReader sr = new StreamReader(fs);
                    lasm = sr.ReadToEnd();
                    sr.Close();
                    fs.Close();
                }
                int linenumber;
                string[] lines = lasm.Split('\n');
                bool execute = true, main = false;
                for (linenumber = 0; linenumber < lines.Length; linenumber++)
                {
                    if (output)
                    {
                        UTF8Encoding enc = new UTF8Encoding();
                        Console.WriteLine(App(lines[linenumber]));
                    }
                    else
                    {
                        try
                        {
                            string line = lines[linenumber].Trim(), ac = "";
                            bool acs = false;
                            short oldmode = mode;
                            switch (line)
                            {
                                case ".data":
                                    mode = 1;
                                    break;
                                case ".code":
                                    mode = 2;
                                    break;
                            }
                            if (oldmode == mode)
                            {
                                switch (mode)
                                {
                                    case 1:
                                        #region Variables
                                        // Set var
                                        string vname = "";
                                        foreach (char c in line)
                                        {
                                            string[] linex;
                                            if (c == '\'' || c == '\"') acs = !acs;
                                            else
                                            {
                                                if (acs) ac += c;
                                                else
                                                {
                                                    linex = line.Split(':');
                                                    vname = linex[0];
                                                }
                                            }
                                        }
                                        if (ac == "$newline") ac = Environment.NewLine;
                                        vars.Add(vname, ac);
                                        //Console.WriteLine("VAR[{0}]={1}", vname, ac);
                                        break;
                                        #endregion
                                    case 2:
                                        #region Runtime
                                        #region Definition
                                        // Run code
                                        string[] argx = null;
                                        object[] argxx = null;
                                        object[] temp = null;
                                        int gint = 0;
                                        string gstr = "";
                                        try
                                        {
                                            argx = line.Split(' ');
                                            for (int i = 1; i < argx.Length; i++)
                                            {
                                                gstr += argx[i] + ' ';
                                            }
                                            argxx = gstr.Split(',');
                                            for (int i = 0; i < argxx.Length; i++)
                                            {
                                                ((IList)temp).Add(argxx[i].ToString().Trim());
                                            }
                                            argxx = temp;
                                        }
                                        catch { }
                                        #endregion
                                        #region Function
                                        if (!line.StartsWith(";") && execute == true)
                                        {
                                            switch (argx[0].ToLower())
                                            {
                                                #region Functions
                                                case "mov":
                                                    #region mov
                                                    switch ((string)argxx[0])
                                                    {
                                                        case "eax": eax = argxx[1]; break;
                                                        case "ebx": ebx = argxx[1]; break;
                                                        case "ecx": ecx = argxx[1]; break;
                                                        case "edx": edx = argxx[1]; break;
                                                    }
                                                    break;
                                                    #endregion
                                                case "int":
                                                    #region int
                                                    if (((string)argxx[0]).ToLower().EndsWith("h")) gint = int.Parse(((string)argxx[0]).ToUpper().TrimEnd('H'), System.Globalization.NumberStyles.AllowHexSpecifier);
                                                    else gint = int.Parse(argxx[0].ToString());
                                                    switch (gint)
                                                    {
                                                        case 0x80:
                                                            switch (int.Parse(eax.ToString()))
                                                            {
                                                                case 1:
                                                                    breakCode = true;
                                                                    returnCode = int.Parse(ebx.ToString());
                                                                    break;
                                                                case 2:
                                                                    Console.Write(vars[(string)ecx]);
                                                                    break;
                                                                case 3:
                                                                    Console.WriteLine(vars[(string)ecx]);
                                                                    break;
                                                                case 4:
                                                                    Console.Clear();
                                                                    break;
                                                                case 5:
                                                                    Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ecx.ToString());
                                                                    break;
                                                                case 6:
                                                                    Console.BackgroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ecx.ToString());
                                                                    break;
                                                            }
                                                            break;
                                                    }
                                                    break;
                                                    #endregion
                                                case "jmp":
                                                    #region jmp
                                                    if (!int.TryParse(argxx[0].ToString(), out linenumber))
                                                        linenumber = labels[(string)argxx[0]];
                                                    break;
                                                    #endregion
                                                case "add":
                                                    #region add
                                                    gint = int.Parse(vars[(string)argxx[0]]);
                                                    gint += int.Parse(argxx[1].ToString());
                                                    vars[(string)argxx[0]] = gint.ToString();
                                                    break;
                                                    #endregion
                                                case "dec":
                                                    #region dec
                                                    gint = int.Parse(vars[(string)argxx[0]]);
                                                    gint -= int.Parse(argxx[1].ToString());
                                                    vars[(string)argxx[0]] = gint.ToString();
                                                    break;
                                                    #endregion
                                                case "mul":
                                                    #region mul
                                                    gint = int.Parse((string)argxx[1]);
                                                    gint *= int.Parse(argxx[2].ToString());
                                                    vars[(string)argxx[0]] = gint.ToString();
                                                    break;
                                                    #endregion
                                                case "div":
                                                    #region div
                                                    gint = int.Parse((string)argxx[1]);
                                                    gint /= int.Parse(argxx[2].ToString());
                                                    vars[(string)argxx[0]] = gint.ToString();
                                                    break;
                                                    #endregion
                                                case "cmp":
                                                    #region cmp
                                                    string nextPlace = "";
                                                    int argx1, argx2;
                                                    string arg1, arg2;
                                                    if (!int.TryParse(argxx[0].ToString(), out argx1)) arg1 = vars[(string)argxx[0]];
                                                    else arg1 = argx1.ToString();
                                                    if (!int.TryParse(argxx[1].ToString(), out argx2)) arg2 = vars[(string)argxx[1]];
                                                    else arg2 = argx2.ToString();
                                                    if (arg1 == arg2) nextPlace = (string)argxx[2];
                                                    else if (arg1 != arg2 && argxx.Length == 4) nextPlace = (string)argxx[3];
                                                    if (nextPlace.Length > 0)
                                                    {
                                                        if (!int.TryParse(nextPlace, out linenumber))
                                                            linenumber = labels[nextPlace];
                                                    }
                                                    break;
                                                    #endregion
                                                case "inp":
                                                    #region inp
                                                    string input = Console.ReadLine();
                                                    try { vars[(string)argxx[0]] = input; }
                                                    catch { }
                                                    break;
                                                    #endregion
                                                case "del":
                                                    #region del
                                                    vars.Remove((string)argxx[0]);
                                                    break;
                                                    #endregion
                                                #endregion
                                            }
                                        }
                                        #endregion
                                        #region Label
                                        else if (line.StartsWith(";"))
                                        {
                                            // It's a label, mark it's line for future reference
                                            if (!labels.ContainsKey(line.TrimStart(';'))) labels.Add(line.TrimStart(';'), linenumber);
                                            if (line.TrimStart(';').ToLower() == "main" && !main) { execute = true; main = true; }
                                            else execute = false;
                                        }
                                        #endregion
                                        break;
                                        #endregion
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            breakCode = true;
                            returnCode = 1;
                            Console.WriteLine("An exception occoured on line '{0}' at internal method '{1}':", linenumber, e.StackTrace);
                            Console.WriteLine(e.Message);
                        }
                    }
                    if (breakCode) break;
                }
#if DEBUG
                Console.ForegroundColor = ConsoleColor.White;
                Console.BackgroundColor = ConsoleColor.Black;
                Console.WriteLine("");
                Console.WriteLine("===============================");
                Console.WriteLine("Application returned code '{0}'", returnCode);
                Console.WriteLine("DEBUG::Press any key to continue...");
                Console.ReadKey();
#endif
                if (output)
                {
                    outsw.Close();
                    outfs.Close();
                    Console.SetOut(outold);
                    Console.WriteLine("Output file written as '{0}'.", outfile);
                }
                return returnCode;
                #endregion
            }
            else
            {
                Console.WriteLine("Usage: easm [-exec output.out] target-file.easm [output.out]");
                return 0;
            }
        }
        static string App(string input)
        {
            return Convert.ToBase64String(new UTF8Encoding().GetBytes(input));
        }
        static string Open(string input)
        {
            return new UTF8Encoding().GetString(Convert.FromBase64String(input));
        }
    }
    public class Execute
    {
        static Dictionary<string, Type> funcs = new Dictionary<string, Type>();
        static Dictionary<string, string> vars;
        
        static Dictionary<string, string> regs;
        static Dictionary<string, int> labels;
        public static int ExecuteFile(string file, TextWriter console_out, TextReader console_in)
        {
            #region Execution
            Console.SetIn(console_in);
            Console.SetOut(console_out);
            int returnCode = 0;
            bool breakCode = false;
            vars = new Dictionary<string, string>();
            regs = new Dictionary<string, string>();
            labels = new Dictionary<string, int>();
            object eax = "1", ebx = "0", ecx = "", edx = "";
            FileStream outfs = null;
            StreamWriter outsw = null;
            TextWriter outold = null;
            short mode = 0; // 0 = none, 1 = .data, 2 = .code, 3 = .lib, 4 = .inc
            string lasm = "";
            FileStream fs = new FileStream(file, FileMode.Open);
            StreamReader sr = new StreamReader(fs);
            lasm = sr.ReadToEnd();
            sr.Close();
            fs.Close();
            int linenumber;
            short preIncLibmode = 0;
            string[] lines = lasm.Split('\n');
            bool execute = true, main = false;
            for (linenumber = 0; linenumber < lines.Length; linenumber++)
            {
                try
                {
                    string line = lines[linenumber], ac = "";
                Proc:
                    line = line.Trim();
                    string[] parts = line.Split(' ');
                    string lineStart = parts[0];
                    string lineRest = "";
                    for (int i = 1; i < parts.Length; i++)
                    {
                        lineRest += parts[i] + " ";
                    }
                    lineRest.Trim();
                    ac = "";
                    bool acs = false;
                    short oldmode = mode;
                    switch (lineStart)
                    {
                        case ".data":
                            mode = 1;
                            break;
                        case ".code":
                            mode = 2;
                            break;
                        case ".lib":
                            mode = 3;
                            preIncLibmode = oldmode;
                            break;
                        case ".inc":
                            mode = 4;
                            preIncLibmode = oldmode;
                            break;
                    }
                    if (oldmode == mode)
                    {
                        switch (mode)
                        {
                            case 1:
                                #region Variables
                                // Set var
                                string vname = "";
                                string[] subline = line.Split('\n');
                                foreach (string sline in subline)
                                {
                                    ac = "";
                                    foreach (char c in sline)
                                    {
                                        string[] linex;
                                        if (c == '\'' || c == '\"') acs = !acs;
                                        else
                                        {
                                            if (acs) ac += c;
                                            else
                                            {
                                                linex = sline.Split(':');
                                                vname = linex[0];
                                            }
                                        }
                                    }
                                    if (ac == "$newline") ac = Environment.NewLine;
                                    if (!vars.ContainsKey(vname)) vars.Add(vname, ac);
                                    else vars[vname] = ac;
                                }
                                //Console.WriteLine("VAR[{0}]={1}", vname, ac);
                                break;
                                #endregion
                            case 2:
                                #region Runtime
                                #region Definition
                                // Run code
                                string[] argx = null;
                                object[] argxx = null;
                                int gint;
                                string gstr;
                                try
                                {
                                    argx = line.Split(' ');
                                    argxx = argx[1].Split(',');
                                }
                                catch { }
                                #endregion
                                #region Function
                                if (!line.StartsWith(";") && execute == true)
                                {
                                    switch (argx[0].ToLower())
                                    {
                                        #region Functions
                                        case "mov":
                                            #region mov
                                            switch ((string)argxx[0])
                                            {
                                                case "eax": eax = argxx[1]; break;
                                                case "ebx": ebx = argxx[1]; break;
                                                case "ecx": ecx = argxx[1]; break;
                                                case "edx": edx = argxx[1]; break;
                                            }
                                            break;
                                            #endregion
                                        case "int":
                                            #region int
                                            if (((string)argxx[0]).ToLower().EndsWith("h")) gint = int.Parse(((string)argxx[0]).ToUpper().TrimEnd('H'), System.Globalization.NumberStyles.AllowHexSpecifier);
                                            else gint = int.Parse(argxx[0].ToString());
                                            switch (gint)
                                            {
                                                case 0x80:
                                                    switch (int.Parse(eax.ToString()))
                                                    {
                                                        case 1:
                                                            breakCode = true;
                                                            returnCode = int.Parse(ebx.ToString());
                                                            break;
                                                        case 2:
                                                            Console.Write(vars[(string)ecx]);
                                                            break;
                                                        case 3:
                                                            Console.WriteLine(vars[(string)ecx]);
                                                            break;
                                                        case 4:
                                                            Console.Clear();
                                                            break;
                                                        case 5:
                                                            Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ecx.ToString());
                                                            break;
                                                        case 6:
                                                            Console.BackgroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ecx.ToString());
                                                            break;
                                                    }
                                                    break;
                                            }
                                            break;
                                            #endregion
                                        case "jmp":
                                            #region jmp
                                            if (!int.TryParse(argxx[0].ToString(), out linenumber))
                                                linenumber = labels[(string)argxx[0]];
                                            break;
                                            #endregion
                                        case "add":
                                            #region add
                                            gint = int.Parse(vars[(string)argxx[0]]);
                                            gint += int.Parse(argxx[1].ToString());
                                            vars[(string)argxx[0]] = gint.ToString();
                                            break;
                                            #endregion
                                        case "dec":
                                            #region dec
                                            gint = int.Parse(vars[(string)argxx[0]]);
                                            gint -= int.Parse(argxx[1].ToString());
                                            vars[(string)argxx[0]] = gint.ToString();
                                            break;
                                            #endregion
                                        case "mul":
                                            #region mul
                                            gint = int.Parse((string)argxx[1]);
                                            gint *= int.Parse(argxx[2].ToString());
                                            vars[(string)argxx[0]] = gint.ToString();
                                            break;
                                            #endregion
                                        case "div":
                                            #region div
                                            gint = int.Parse((string)argxx[1]);
                                            gint /= int.Parse(argxx[2].ToString());
                                            vars[(string)argxx[0]] = gint.ToString();
                                            break;
                                            #endregion
                                        case "cmp":
                                            #region cmp
                                            string nextPlace = "";
                                            int argx1, argx2;
                                            string arg1, arg2;
                                            if (!int.TryParse(argxx[0].ToString(), out argx1)) arg1 = vars[(string)argxx[0]];
                                            else arg1 = argx1.ToString();
                                            if (!int.TryParse(argxx[1].ToString(), out argx2)) arg2 = vars[(string)argxx[1]];
                                            else arg2 = argx2.ToString();
                                            if (arg1 == arg2) nextPlace = (string)argxx[2];
                                            else if (arg1 != arg2 && argxx.Length == 4) nextPlace = (string)argxx[3];
                                            if (nextPlace.Length > 0)
                                            {
                                                if (!int.TryParse(nextPlace, out linenumber))
                                                    linenumber = labels[nextPlace];
                                            }
                                            break;
                                            #endregion
                                        case "inp":
                                            #region inp
                                            string input = Console.ReadLine();
                                            try { vars[(string)argxx[0]] = input; }
                                            catch { }
                                            break;
                                            #endregion
                                        case "del":
                                            #region del
                                            vars.Remove((string)argxx[0]);
                                            break;
                                            #endregion
                                        #endregion
                                    }
                                }
                                #endregion
                                #region Label
                                else if (line.StartsWith(";"))
                                {
                                    // It's a label, mark it's line for future reference
                                    if (!labels.ContainsKey(line.TrimStart(';'))) labels.Add(line.TrimStart(';'), linenumber);
                                    if (line.TrimStart(';').ToLower() == "main" && !main) { execute = true; main = true; }
                                    else execute = false;
                                }
                                #endregion
                                break;
                                #endregion
                            case 3:
                                #region Libraries
                                break;
                                #endregion
                            case 4:
                                #region Includes
                                try
                                {
                                    fs = new FileStream(line, FileMode.Open);
                                    sr = new StreamReader(fs);
                                    line = sr.ReadToEnd();
                                    sr.Close();
                                    fs.Close();
                                    mode = preIncLibmode;
                                    goto Proc;
                                }
                                catch
                                {
                                    Console.WriteLine("Could not find include file \"{0}\".", lineRest);
                                }
                                break;
                                #endregion
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    breakCode = true;
                    returnCode = 1;
                    Console.WriteLine("An exception occoured on line '{0}' at internal method '{1}':", linenumber, e.StackTrace);
                    Console.WriteLine(e.Message);
                }
                if (breakCode) break;
            }
#if DEBUG
                Console.ForegroundColor = ConsoleColor.White;
                Console.BackgroundColor = ConsoleColor.Black;
                Console.WriteLine("");
                Console.WriteLine("===============================");
                Console.WriteLine("Application returned code '{0}'", returnCode);
                Console.WriteLine("DEBUG::Press any key to continue...");
                Console.ReadKey();
#endif
            return returnCode;
            #endregion
        }
        public static void ClearExternalFunctions()
        {
            funcs.Clear();
        }
        public static void LoadExternalFunctions(string file)
        {
            if (File.Exists(file))
            {
                Assembly asm = Assembly.LoadFile(file);
                foreach (Type t in asm.GetTypes())
                {
                    List<string> interfaces = new List<string>();
                    foreach (Type i in t.GetInterfaces())
                    {
                        interfaces.Add(i.Name);
                    }
                    if (interfaces.Contains("IEASMFunction"))
                    {
                        object obj = t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
                        string name = (string)t.GetMethod("GetMnemonic").Invoke(obj, new object[] { });
                        if (!funcs.ContainsKey(name))
                        {
                            funcs.Add(name, t);
                        }
                    }
                }
            }
        }
    }
    public interface IEASMFunction
    {
        /// <summary>
        /// Returns the Mnemonic of the function
        /// </summary>
        string GetMnemonic();
        /// <summary>
        /// Call the function with the arguments
        /// </summary>
        object Call(params object[] args);
    }
    public class TestFunction : IEASMFunction
    {
        public object Call(params object[] args)
        {
            Console.WriteLine("ARGUMENTS:");
            foreach (object o in args)
            {
                Console.WriteLine(o);
            }
            return null;
        }
        public string GetMnemonic()
        {
            return "test";
        }
    }
    public class DemoFunction : IEASMFunction
    {
        public object Call(params object[] args)
        {
            Console.WriteLine("ARGUMENTS:");
            foreach (object o in args)
            {
                Console.WriteLine(o);
            }
            return null;
        }
        public string GetMnemonic()
        {
            return "demo";
        }
    }
}